package org.aplombh.java.awcing.basic.basics.highAccuracyAlgorithm;

import java.util.ArrayDeque;
import java.util.HashMap;
import java.util.Stack;
import java.util.stream.IntStream;

/**
 * high accuracy algorithm for large numbers
 */
public class MyHighAccuracyAlgorithm {
    /**
     * Addition between two large numbers
     * 两大数相加
     *
     * @param a first addend 第一个的加数
     * @param b second addend 第二个大加数
     * @return sum 和
     */
    public static String accuracyAdd(String a, String b) {
        // Store these numbers
        // 存储这些大数
        Stack<Integer> stackA = new Stack<>();
        Stack<Integer> stackB = new Stack<>();
        Stack<Integer> stackC = new Stack<>();
        // Record the returned number at the end
        // 记录最后返回的数
        StringBuilder stringBuilder = new StringBuilder();

        // Through each digit of the string in reverse order and store on the stack
        // 逆序遍历整个string的每一位，并存储进stack中
        for (int i = a.length() - 1; i >= 0; i--) stackA.push(a.charAt(i) - '0');
        for (int i = a.length() - 1; i >= 0; i--) stackB.push(b.charAt(i) - '0');

        // Record the sum and overflows of the current bit
        // 记录当前位的和和溢出位
        int t = 0;
        // compute
        // 计算
        for (int i = 0; i < stackA.size() || i < stackB.size(); i++) {

            // Add up the current bit
            // 把当前位相加
            if (i < stackA.size()) t += stackA.get(i);
            if (i < stackB.size()) t += stackB.get(i);

            // Records the sum of the current bit
            // 记录当前位的和
            stackC.push(t % 10);
            // Records the number of overflows
            // 记录溢出位
            t /= 10;
        }
        // Put data into a string
        // 将data存入string中
        while (!stackC.empty()) {
            stringBuilder.append(stackC.pop());
        }
        return stringBuilder.toString();
    }

    /**
     * Subtraction between two large numbers
     * 两大数相减
     *
     * @param a minuend 被减数
     * @param b subtrahend 减数
     * @return difference 差
     */
    public static String accuracySub(String a, String b) {
        // Store these numbers
        // 存储这些大数
        Stack<Integer> stackA = new Stack<>();
        Stack<Integer> stackB = new Stack<>();
        Stack<Integer> stackC = new Stack<>();
        // Record the returned number at the end
        // 记录最后返回的数
        StringBuilder stringBuilder = new StringBuilder();

        // Through each digit of the string in reverse order and store on the stack
        // 逆序遍历整个string的每一位，并存储进stack中
        for (int i = a.length() - 1; i >= 0; i--) stackA.push(a.charAt(i) - '0');
        for (int i = a.length() - 1; i >= 0; i--) stackB.push(b.charAt(i) - '0');

        // Compare the size of a and b
        // 比较a和b的大小
        boolean flag = true;
        if (stackA.size() != stackB.size()) flag = stackA.size() > stackB.size();
        else for (int i = stackA.size() - 1; i >= 0; i--)
            if (!stackA.get(i).equals(stackB.get(i))) flag = stackA.get(i) - stackB.get(i) > 0;

        // If b is greater than a, the positions of A and B are reversed, And add a "-" sign to the result
        // 如果b比a大，A和B的位置互换，并且添加 "-" 号
        if (!flag) {
            Stack<Integer> temp = stackA;
            stackA = stackB;
            stackB = temp;
            // Put "-" into a string
            // 字符串中添加 "-"
            stringBuilder.append("-");
        }

        // Store the difference and borrow of the current bit
        // 存储当前位的差和借位
        int t = 0;
        for (int i = 0; i < stackA.size(); i++) {
            // Subtract the current bit
            // 当前位相减
            t = stackA.get(i) - t;
            if (i < stackB.size()) t -= stackB.get(i);
            // Records the sum of the current bit
            // 记录当前位的和
            stackC.push((t + 10) % 10);
            // Records the number of the borrow
            // 记录借位
            if (t < 0) t = 1;
            else t = 0;
        }
        // Put data into a string
        // 将数据加入字符串中
        while (!stackC.empty()) {
            stringBuilder.append(stackC.pop());
        }
        return stringBuilder.toString();
    }

    /**
     * Multiplication between two large numbers
     * 两大数相乘
     *
     * @param a first multiplier
     * @param b second multiplier
     * @return sum
     */
    public static String accuracyMul(String a, int b) {
        // Store these numbers
        // 存储这些大数
        Stack<Integer> stackA = new Stack<>();
        Stack<Integer> stackC = new Stack<>();
        // Record the returned number at the end
        // 记录最后返回的数
        StringBuilder stringBuilder = new StringBuilder();

        // Through each digit of the string in reverse order and store on the stack
        // 逆序遍历整个string的每一位，并存储进stack中
        IntStream.range(0, a.length()).map(i -> a.length() - i - 1).forEach(i -> stackA.push(a.charAt(i) - '0'));

        // Record the Product and overflows of the current bit
        // 记录当前位的乘积和溢出位
        int t = 0;
        // compute
        // 计算
        for (int i = 0; i < stackA.size() || t != 0; i++) {

            // The current bit is multiplied by the multiplier
            // 当前位乘以乘数
            if (i < stackA.size()) t += stackA.get(i) * b;

            // Records the product of the current bit
            // 记录当前位的乘积
            stackC.push(t % 10);
            // Records the number of overflows
            // 记录溢出位
            t /= 10;
        }
        // Delete the 0 before the number
        // 删除数组前面的0
        while (!stackC.empty() && stackC.peek() == 0) {
            stackC.pop();
        }
        // Put data into a string
        // 将data存入string中
        while (!stackC.empty()) {
            stringBuilder.append(stackC.pop());
        }
        return stringBuilder.toString();
    }

    /**
     * Division between two numbers
     * 两数相除
     *
     * @param a first dividend 除数
     * @param b second divisor 被除数
     * @return quotient 商
     */
    public static HashMap<String, String> accuracyDiv(String a, int b) {
        // Store these numbers
        // 存储这些大数
        Stack<Integer> stackA = new Stack<>();
        ArrayDeque<Integer> dequeC = new ArrayDeque<>();
        HashMap<String, String> map = new HashMap<>();
        // Record the returned number at the end
        // 记录最后返回的数
        StringBuilder stringBuilder = new StringBuilder();

        // Through each digit of the string in reverse order and store on the stack
        // 逆序遍历整个string的每一位，并存储进stack中
        IntStream.range(0, a.length()).map(i -> a.length() - i - 1).forEach(i -> stackA.push(a.charAt(i) - '0'));

        // store the quotient and remainder of the current bit
        // 记录上一位的余数
        int r = 0;
        // compute
        // 计算
        for (int i = stackA.size() - 1; i >= 0; i--) {

            // Current bit plus the remainder divided by b
            // 当前位加上余数除b
            r = r * 10 + stackA.get(i);
            // Records the quotient of the current bit
            // 记录当前位的商
            dequeC.push(r / b);
            // Records the number of remainder
            // 记录余数
            r %= b;
        }

        // Delete the 0 before the number
        // 删除数前的余数
        while (dequeC.size() > 1 && dequeC.peekLast() == 0) dequeC.removeLast();

        // Put data into a string
        // 将data存入string中
        while (!dequeC.isEmpty()) stringBuilder.append(dequeC.removeLast());

        map.put("quotient", stringBuilder.toString());
        map.put("remainder", Integer.toString(r));
        return map;
    }
}
